/**
 * VideoUtils.java 
 * Copyright © 2015-3015, 杭州泊享网络科技有限公司
 * 
 * @author Liu Tao
 * @create 2014年11月11日
 */
package com.pshare.util.video;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

import org.apache.log4j.Logger;

import com.coremedia.iso.IsoFile;
import com.coremedia.iso.boxes.Container;
import com.googlecode.mp4parser.authoring.Movie;
import com.googlecode.mp4parser.authoring.Track;
import com.googlecode.mp4parser.authoring.builder.DefaultMp4Builder;
import com.googlecode.mp4parser.authoring.container.mp4.MovieCreator;
import com.googlecode.mp4parser.authoring.tracks.CroppedTrack;

/**
 * MP4工具类
 */
public class Mp4Handler {

	private static final Logger logger = Logger.getLogger(Mp4Handler.class);

	public static void main(String[] args) {
		try {
			Mp4Handler.clipVideo("/Users/liutao/Documents/video/from/2.mov", "/Users/liutao/Documents/video/to", 0, 8);
//			VideoUtils.clipVideo("/Users/liutao/Documents/video/from/1.mp4", "/Users/liutao/Documents/video/to", 0, 38);
//			 double length1 = VideoUtils.getVideoDetailLenth("/Users/liutao/Documents/video/from/1.MOV");
//			 int length =
//			 VideoUtils.getVideoLenth("/Users/liutao/Documents/video/from/1.MOV", true);
//			 System.out.println(length + "---" + length1);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 获取视频长度，单位秒
	 * 
	 * @param path
	 * @param getShort 是否取整数部分，比如8.6秒，当做8秒处理
	 * @return int
	 * @throws IOException
	 */
	public static int getVideoLenth(String path, boolean getShort) throws IOException {
		double length = getVideoDetailLenth(path);
		int l = (int) length;
		if (getShort) {
			return l;
		} else {
			return length > l ? l + 1 : l;
		}
	}

	/**
	 * 获取视频长度，单位秒
	 * 
	 * @param path
	 * @return double
	 * @throws IOException
	 */
	public static double getVideoDetailLenth(String path) throws IOException {
		IsoFile isoFile = new IsoFile(path);
        double length = (double)
                isoFile.getMovieBox().getMovieHeaderBox().getDuration() /
                isoFile.getMovieBox().getMovieHeaderBox().getTimescale();
		return length;
	}

	/**
	 * 截取指定时间段的视频
	 * 
	 * @param srcPath 原视频的路径
	 * @param dstPath 存放截取后视频的路径
	 * @param begin 需要截取的开始时间，单位秒
	 * @param end 截取的结束时间
	 * @throws IOException
	 */
	public static void clipVideo(String srcPath, String dstPath, double begin, double end) throws IOException {
		
		Movie movie =  MovieCreator.build(srcPath);
		
		List<Track> tracks = movie.getTracks();
		movie.setTracks(new LinkedList<Track>());

		double startTime1 = begin;
		double endTime1 = end;
		boolean timeCorrected = false;

		for (Track track : tracks) {
			if (track.getSyncSamples() != null && track.getSyncSamples().length > 0) {
				if (timeCorrected) {
					logger.info("The startTime has already been corrected by another track with SyncSample. Not Supported.");
					throw new RuntimeException(
							"The startTime has already been corrected by another track with SyncSample. Not Supported.");
				}
				// true,false表示短截取；false,true表示长截取
				startTime1 = correctTimeToSyncSample(track, startTime1, false);
				endTime1 = correctTimeToSyncSample(track, endTime1, true);
				timeCorrected = true;
			}
		}

		for (Track track : tracks) {
			long currentSample = 0;
			double currentTime = 0;
			double lastTime = 0;
			long startSample1 = 0;
			long endSample1 = -1;

			for (int i = 0; i < track.getSampleDurations().length; i++) {
				long delta = track.getSampleDurations()[i];
				if (currentTime > lastTime && currentTime <= startTime1) {
					startSample1 = currentSample;
				}
				if (currentTime > lastTime && currentTime <= endTime1) {
					endSample1 = currentSample;
				}
				lastTime = currentTime;
				currentTime += (double) delta / (double) track.getTrackMetaData().getTimescale();
				currentSample++;
			}
			movie.addTrack(new CroppedTrack(track, startSample1, endSample1));
		}
		long start1 = System.currentTimeMillis();
		Container out = new DefaultMp4Builder().build(movie);
		long start2 = System.currentTimeMillis();
		
		File f = new File(dstPath);
		if (!f.exists()) {
			f.mkdir();
		}
		String dstFileName = String.format("output-%f-%f.mp4", startTime1, endTime1);
		FileOutputStream fos = new FileOutputStream(f.getAbsolutePath() + File.separator + dstFileName);
		FileChannel fc = fos.getChannel();
		out.writeContainer(fc);
		fc.close();
		fos.close();
		long start3 = System.currentTimeMillis();
		logger.info("Building IsoFile took : " + (start2 - start1) + "ms");
		logger.info("Writing IsoFile took : " + (start3 - start2) + "ms");
		logger.info("FileName : " + dstFileName);
	}

	private static double correctTimeToSyncSample(Track track, double cutHere, boolean next) {
		double[] timeOfSyncSamples = new double[track.getSyncSamples().length];
		long currentSample = 0;
		double currentTime = 0;
		for (int i = 0; i < track.getSampleDurations().length; i++) {
			long delta = track.getSampleDurations()[i];

			if (Arrays.binarySearch(track.getSyncSamples(), currentSample + 1) >= 0) {
				// samples always start with 1 but we start with zero therefore
				// +1
				timeOfSyncSamples[Arrays.binarySearch(track.getSyncSamples(), currentSample + 1)] = currentTime;
			}
			currentTime += (double) delta / (double) track.getTrackMetaData().getTimescale();
			currentSample++;

		}
		double previous = 0;
		for (double timeOfSyncSample : timeOfSyncSamples) {
			if (timeOfSyncSample > cutHere) {
				if (next) {
					return timeOfSyncSample;
				} else {
					return previous;
				}
			}
			previous = timeOfSyncSample;
		}
		return timeOfSyncSamples[timeOfSyncSamples.length - 1];
	}

}
